// 1. 定义接口

/**
 * LabeledValue接口就好比一个名字，用来描述上面例子里的要求。
 * 它代表了有一个label属性且类型为string的对象。
 * 需要注意的是，我们在这里并不能像在其它语言里一样，说传给printLabel的对象实现了这个接口。
 * 我们只会去关注值的外形。 只要传入的对象满足上面提到的必要条件，那么它就是被允许的。
 *
 * 还有一点值得提的是，类型检查器不会去检查属性的顺序，只要相应的属性存在并且类型也是对的就可以。
 */
interface LabeledValue {
  name: string
  label: string
}

function printLabel(labeledObj: LabeledValue) {
  console.log(labeledObj.name)
  console.log(labeledObj.label)
}

printLabel({ name: 'zs', label: '123' })

// 2. 可选属性
// 接口里的属性不全都是必需的。

interface SquareConfig {
  color?: string
  width: number
}

function createSquare(config: SquareConfig): { color: string; area: number } {
  return {
    color: config.color ? config.color : 'black',
    area: config.width * 10
  }
}

// 这里传参就可以只传一个
const square = createSquare({ width: 10 })
console.log(square)

// 3. 只读属性
// 一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用readonly来指定只读属性。
interface Point {
  readonly x: number
  readonly y: number
}

const p1: Point = { x: 1.5, y: 2.3 }
//  p1.x = 1.6 这行代码会报错

console.log('p1 =', p1)

// TypeScript 具有ReadonlyArray<T>类型，它与Array<T>相似，只是把所有可变方法去掉了，因此可以确保数组创建后再也不能被修改：

let a: number[] = [1, 2, 3, 4]
let ro: ReadonlyArray<number> = a
// ro[0] = 12  // error
// ro.push(5)  // error
// a = ro      // error 也不能直接赋值

// 但是 ReadonlyArray 经过类型转换后就可以了
a = <number[]>ro
console.log('a =', a)
a = ro as number[]
console.log('a =', a)

// readonly vs const
// 最简单判断该用readonly还是const的方法是看要把它做为变量使用还是做为一个属性。
// 做为变量使用的话用const，若做为属性则使用readonly。

// 4. 函数类型
interface SearchFunc {
  a?: number
  (src: string, sub: string): boolean
  (x: number): number
}

